﻿namespace NJQuery {
	using System;
	using System.Web.UI;
	using System.ComponentModel;
	using System.Diagnostics.CodeAnalysis;
	using System.Collections.Specialized;

	/// <summary>
	/// Base server control used to generate the core functionality of a <see href="http://jqueryui.com/demos/slider/">jQuery UI Slider widget</see>.
	/// </summary>
	public abstract class SliderBase : ManagedWebControl, IPostBackDataHandler {


		#region Constants: Property defaults

		private const bool defaultAnimate = false;
		private const int defaultMax = 100;
		private const int defaultMin = 0;
		private const SliderOrientation defaultOrientation = SliderOrientation.Auto;
		private const int defaultStep = 1;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="SliderBase" /> class.
		/// </summary>
		protected SliderBase() : base(HtmlTextWriterTag.Div) { }

		#endregion


		#region Properties: Widget initialization options

		/// <summary>
		/// Whether to slide handle smoothly when user clicks outside handle on the bar.
		/// This property maps directly to the "animate" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultAnimate), Category("Behavior")]
		public virtual bool Animate {
			get { return ViewState["Animate"] as bool? ?? defaultAnimate; }
			set { ViewState["Animate"] = value; }
		}

		/// <summary>
		/// The maximum value of the slider.
		/// This property maps directly to the "max" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultMax), Category("Behavior")]
		public virtual int Max {
			get { return ViewState["Max"] as int? ?? defaultMax; }
			set { ViewState["Max"] = value; }
		}

		/// <summary>
		/// The minimum value of the slider.
		/// This property maps directly to the "min" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultMin), Category("Behavior")]
		public virtual int Min {
			get { return ViewState["Min"] as int? ?? defaultMin; }
			set { ViewState["Min"] = value; }
		}

		/// <summary>
		/// Normally you don't need to set this property because the plugin detects the slider orientation automatically. If the orientation is not correctly detected, you can set this property to "Horizontal" or "Vertical".
		/// This property maps directly to the "orientation" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultOrientation), Category("Appearance")]
		public virtual SliderOrientation Orientation {
			get { return ViewState["Orientation"] as SliderOrientation? ?? defaultOrientation; }
			set {
				if(!Enum.IsDefined(typeof(SliderOrientation), value)) throw new ArgumentException("Value not defined.", "value");
				ViewState["Orientation"] = value;
			}
		}

		/// <summary>
		/// Determines the size or amount of each interval or step the slider takes between <see cref="Min"/> and <see cref="Max"/>. The full specified value range of the slider (<see cref="Max"/> - <see cref="Min"/>) needs to be evenly divisible by the step.
		/// This property maps directly to the "step" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultStep), Category("Behavior")]
		[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Step", Justification = "Exposed as the jQuery option name.")]
		public virtual int Step {
			get { return ViewState["Step"] as int? ?? defaultStep; }
			set { ViewState["Step"] = value; }
		}

		#endregion


		#region Events

		/// <summary>
		/// Occurs on postback when the <see cref="Slider.Value">Slider.Value</see> or <see cref="SliderRange.StartValue">SliderRange.StartValue</see>/<see cref="SliderRange.EndValue">SliderRange.EndValue</see> property values have changed since previous render.
		/// </summary>
		[Category("Action")]
		public event EventHandler ValueChanged;

		#endregion


		#region Methods: Event hooks

		/// <summary>
		/// Raises the <see cref="ValueChanged"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
		protected virtual void OnValueChanged(EventArgs e) {
			if(ValueChanged != null) ValueChanged.Invoke(this, e);
		}

		#endregion


		#region Method: Rendering hooks

		/// <summary>
		/// Raises the <see cref="Control.PreRender"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object containing the event data.</param>
		protected override void OnPreRender(EventArgs e) {
			base.OnPreRender(e);

			if(Visible) Page.RegisterRequiresPostBack(this);
		}

		/// <summary>
		/// Allows a jQuery server control to reference scripts and stylesheets necessary to render their client markup and widgets.
		/// </summary>
		/// <param name="manager">The <see cref="Manager"/> used to handle style and script referencing in addition to unified generation of load and ready scripts.</param>
		protected override void Register(Manager manager) {

			manager.RegisterClientScript(ClientScript.Core);
			manager.RegisterClientScript(ClientScript.UICore);
			manager.RegisterClientScript(ClientScript.UIWidgetFactory);
			manager.RegisterClientScript(ClientScript.UIMouseUtility);
			manager.RegisterClientScript(ClientScript.UISliderWidget);

			manager.RegisterClientStyle(ClientStyle.UICore);
			manager.RegisterClientStyle(ClientStyle.UISliderWidget);
			manager.RegisterTheme();

			manager.RegisterForReadyScript(this);
		}

		/// <summary>
		/// Renders the jQuery "Ready Event" script required to generate the client-side jQuery widget.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected override void RenderReadyScript(ScriptBuilder script) {
			RenderInitScript(script);
		}

		/// <summary>
		/// Renders the jQuery UI initialization script.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected virtual void RenderInitScript(ScriptBuilder script) {

			script.RenderIdSelector(ClientID);
			script.Append(".slider(");

			PropertyCollection properties = script.CreateProperties();
			AddInitProperties(properties);
			script.RenderProperties(properties);

			script.Append(");");
		}

		/// <summary>
		/// Creates the properties object that will be rendered as the jQuery UI initialization options.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="properties">A <see cref="PropertyCollection"/> object that will become the jQuery UI initialization options.</param>
		protected virtual void AddInitProperties(PropertyCollection properties) {

			bool animate = Animate;
			if(animate != defaultAnimate) properties.AddValue("animate", animate);

			int max = Max;
			if(max != defaultMax) properties.AddValue("max", max);

			int min = Min;
			if(min != defaultMin) properties.AddValue("min", Min);

			SliderOrientation orientation = Orientation;
			if(orientation != defaultOrientation) properties.AddEnumValue("orientation", orientation);

			int step = Step;
			if(step != defaultStep) properties.AddValue("step", step);

		}

		#endregion


		#region Methods: Post data

		/// <summary>
		/// Processes postback data for an ASP.NET server control.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="postDataKey">The key identifier for the control.</param>
		/// <param name="postCollection">The collection of all incoming name values.</param>
		/// <returns>True if the server control's state changes as a result of the postback; otherwise, false.</returns>
		protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
			return false;
		}

		/// <summary>
		/// Signals the server control to notify the ASP.NET application that the state of the control has changed.
		/// This method is used primarily by control developers.
		/// </summary>
		[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This method is amazing.  You get a complaint if you don't include the protected one, but when you do, it tells you to use events.  I included it simply because it gives the derrived control a chance to hook deeper.")]
		protected virtual void RaisePostDataChangedEvent() { }

		#endregion


		#region Explicit Implementation: IPostBackDataHandler

		bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
			return LoadPostData(postDataKey, postCollection);
		}

		void IPostBackDataHandler.RaisePostDataChangedEvent() {
			RaisePostDataChangedEvent();
		}

		#endregion


	}

}